﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using Microsoft.Win32;

namespace SyslogServer
{

    public partial class Service1 : ServiceBase
    {
        public Socket tcpListener;
        public UdpClient udpListener;
        private const string regRoot = @"HKEY_LOCAL_MACHINE\SOFTWARE\SysLogServer";

        private enum logLevels
	    {
	        none,
            exception,
            error,
            warning,
            info,
            debug
        }
        public int portNumber;
        private logLevels logLevel;
        private string logSource;
        private string logto;
        public IPAddress listenOn;
        private bool protoTCP;
        private bool protoUDP;
        private bool toEventlog;
        private bool toSQLDB;
        private string sqlServer;
        private string sqlDatabase;
        private string connectString;
        private System.Data.SqlClient.SqlConnection dataConnection;

        public Service1()
        {
            InitializeComponent();
            debugLog("Instance initialized", logLevels.debug);
        }

        protected override void OnStart(string[] args)
        {
            debugLog("Service starting...", logLevels.debug);
            startit();
            debugLog("Service own start performed.", logLevels.debug);
            base.OnStart(args);
            debugLog("Service started.", logLevels.debug);
        }

        protected override void OnStop()
        {
            debugLog("Service stop initiated.", logLevels.debug);
            stopit();
            debugLog("Service own stop performed.", logLevels.debug);
            base.OnStop();
            debugLog("Service stopped.", logLevels.debug);
        }

        protected override void OnPause()
        {
            stopit();
            base.OnPause();
        }

        protected override void OnContinue()
        {
            startit();
            base.OnContinue();
        }

        public static void NewConnection(IAsyncResult ar)
        {
            Service1 thisService = (Service1)ar.AsyncState;
            thisService.debugLog("New TCP connection initiated.", logLevels.debug);
            Socket lsnr = thisService.tcpListener;
            Socket handler = lsnr.EndAccept(ar);
            StateObject state = new StateObject();
            state.WorkSocket = thisService;
            handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                new AsyncCallback(ReadCallBack), state);
            thisService.debugLog("Read call back configured.", logLevels.debug);
        }

        public static void ReceiveCallBack(IAsyncResult ar)
        {
            Service1 thisService = (Service1)ar.AsyncState;
            thisService.debugLog("New UDP data receiving", logLevels.debug);
            IPEndPoint groupEP = new IPEndPoint(thisService.listenOn, thisService.portNumber);
            byte[] bytes = thisService.udpListener.Receive(ref groupEP);
            thisService.debugLog("UDP Data received: " + bytes.Length.ToString() + "byte from " +
                groupEP.Address.ToString() + ":" + groupEP.Port.ToString(), logLevels.debug);
            thisService.udpListener.BeginReceive(new AsyncCallback(ReceiveCallBack), thisService);
            thisService.debugLog("New UDP listener process configured.", logLevels.debug);
            string rcvd = Encoding.ASCII.GetString(bytes, 0, bytes.Length);
            thisService.debugLog("UDP received:\r\n" + rcvd, logLevels.debug);
            thisService.HandleMsg(rcvd, groupEP.Address);
        }

        public static void ReadCallBack(IAsyncResult ar)
        {
            StateObject state = (StateObject)ar.AsyncState;
            Service1 thisService = (Service1)state.WorkSocket;
            thisService.debugLog("TCP data receive starting.", logLevels.debug);
            Socket handler = thisService.tcpListener;
            int read = handler.EndReceive(ar);
            if (read > 0)
            {
                state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, read));
                handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReadCallBack), state);
            }
            else
            {
                if (state.sb.Length > 1)
                {
                    thisService.debugLog("TCP: All the data has been read from the client:" +
                        ((System.Net.IPEndPoint)handler.RemoteEndPoint).Address.ToString() + "\r\n" + state.sb.ToString(), logLevels.debug);
                    string rcvd = state.sb.ToString();
                    thisService.HandleMsg(rcvd, ((System.Net.IPEndPoint)handler.RemoteEndPoint).Address);
                }
                handler.Close();
            }

        }

        public void HandleMsg(string rcvd,IPAddress sender)
        {
            debugLog("Start handling received data from " + sender.ToString() + "\r\n" + rcvd, logLevels.debug);
            SyslogMessage mymsg = new SyslogMessage(rcvd);
            mymsg.Sender = sender.ToString();
            debugLog("SyslogMessage object created.", logLevels.debug);
            if (toEventlog)
            {
                debugLog("Sending it to Eventlog", logLevels.debug);
                mymsg.LogIt(logSource);
            }
            if (toSQLDB)
            {
                int waittime = 0;
                if (dataConnection.State == ConnectionState.Broken)
                {
                    dataConnection.Close();
                    while ((dataConnection.State != ConnectionState.Closed) & (waittime < 20))
                    {
                        System.Threading.Thread.Sleep(50);
                        waittime += 1;
                    }
                }
                waittime=0;
                if ((dataConnection.State == ConnectionState.Closed))
                {
                    dataConnection.Open();
                    while ((dataConnection.State != ConnectionState.Open) & (waittime < 20))
                    {
                        System.Threading.Thread.Sleep(50);
                        waittime += 1;
                    }
                }
                waittime = 0;
                if (dataConnection.State != ConnectionState.Open) 
                { 
                    while ((dataConnection.State != ConnectionState.Open) & (waittime<20)) 
                    { 
                        System.Threading.Thread.Sleep(50);
                        waittime += 1;
                    } 
                }
                if (dataConnection.State != ConnectionState.Open)
                {
                    debugLog("Database connection is not Open! The state is: " + dataConnection.State.ToString() + "\r\nSending the message to EventLog", logLevels.warning);
                    mymsg.LogIt(logSource);
                }
                else
                {
                    debugLog("Sending it to SQL db", logLevels.debug);
                    mymsg.ToSQL(dataConnection);
                }
            }
            debugLog("Message handled from "+ sender.ToString() + ":\r\n" + rcvd,logLevels.debug);

        }


        private void debugLog(string message, logLevels msglevel)
        {
            if (msglevel <= logLevel)
            {
                try
                {
                    EventLog.CreateEventSource(logSource, "Application");
                }
                catch
                {
                }
                finally
                {
                    EventLogEntryType thisLogType;
                    switch (msglevel)
                    {
                        case logLevels.none: thisLogType = EventLogEntryType.Error;
                            break;
                        case logLevels.exception: thisLogType = EventLogEntryType.Error;
                            break;
                        case logLevels.error: thisLogType = EventLogEntryType.Error;
                            break;
                        case logLevels.warning: thisLogType = EventLogEntryType.Warning;
                            break;
                        case logLevels.info: thisLogType = EventLogEntryType.Information;
                            break;
                        case logLevels.debug: thisLogType = EventLogEntryType.Information;
                            break;
                        default: thisLogType = EventLogEntryType.SuccessAudit;
                            break;
                    }
                    EventLog.WriteEntry(logSource, message, thisLogType);
                }
            }
        }

        public void startit()
        {
            portNumber = (int)Registry.GetValue(regRoot, "PortNumber", 514);
            logLevel = (logLevels)Registry.GetValue(regRoot, "LogLevel", logLevels.error);
            logSource = (string)Registry.GetValue(regRoot, "Log Source", "Syslog Server");
            logto = (string)Registry.GetValue(regRoot, "Log Name", "Syslog Server");
            if (!EventLog.SourceExists(logSource))
            {
                EventLog.CreateEventSource(logSource, logto);
            }
            listenOn = IPAddress.Any;
            IPAddress.TryParse((string)Registry.GetValue(regRoot, "Listenning IP", "0.0.0.0"), out listenOn);
            protoTCP = (((string)Registry.GetValue(regRoot, "Protocol", "TCP,UDP")).ToLower().Contains("tcp"));
            protoUDP = (((string)Registry.GetValue(regRoot, "Protocol", "TCP,UDP")).ToLower().Contains("udp"));
            toEventlog = (((string)Registry.GetValue(regRoot, "Store in Eventlog", "No")).ToLower().Contains("yes"));
            toSQLDB = (((string)Registry.GetValue(regRoot, "Store in SQL Database", "Yes")).ToLower().Contains("yes"));
            if (toSQLDB)
            {
                sqlServer = (string)Registry.GetValue(regRoot, "SQL Server", "localhost");
                sqlDatabase = (string)Registry.GetValue(regRoot, "SQL Database", "SysLogDB");
                connectString = "Persist Security Info=true;Integrated Security=true;Initial Catalog=" + sqlDatabase + ";server=" + sqlServer;
                dataConnection = new System.Data.SqlClient.SqlConnection(connectString);
                try
                {
                    dataConnection.Open();
                }
                catch (Exception e)
                {
                    toSQLDB = false;
                    debugLog("Error occured opening connection to MSSQL database: " +
                        sqlDatabase + " on server: " + sqlServer + "!\r\n\r\n" +
                        e.Message + "\r\n\r\n" + e.ToString(), logLevels.exception);
                }
            }
            if (protoUDP)
            {
                udpListener = new UdpClient(new IPEndPoint(listenOn, portNumber));
                udpListener.BeginReceive(new AsyncCallback(ReceiveCallBack),this);
            }
            if (protoTCP)
            {
                tcpListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                tcpListener.Bind(new IPEndPoint(listenOn, portNumber));
                tcpListener.Listen(10);
                tcpListener.BeginAccept(new AsyncCallback(NewConnection), this);
            }
        }

        public void stopit()
        {
            udpListener = null;
            //tcpListener.Shutdown(SocketShutdown.Both);
            tcpListener = null;
        }
  }


    
}
